package com.griddroid.serverdaemons;

import java.util.HashMap;
import java.util.Random;
import com.griddroid.configuration.GridServerConfig;
import com.griddroid.nodes.GridClient;
import com.griddroid.nodes.GridServer;
import com.griddroid.serverdaemons.threadhandlers.WorkerHandler;
import com.griddroid.works.WorkRequest;

/**
 * This class represents a WorkerDaemon responsible to manager {@link WorkerHandler WorkerHandlers} 
 * and forward them the available {@link WorkRequest WorkRequests} from the WorkFragmentRequests 
 * collection of the {@link GridServer GridServer}. 
 * 
 * @author Charalampos Mavroforakis
 * @author Thomas Ailianos
 * @author Michail Michailidis
 */
public class WorkerDaemon extends GenericDaemon
{
	private HashMap<String, WorkerHandler> workers;

	/**
	 * Constructs a WorkerDaemon and installs it into the given {@link GridServer GridServer}
	 * @param server
	 * 		server into which will be installed
	 */
	public WorkerDaemon(GridServer server)
	{
		super();
		this.server = server;
		this.server.setWorkerDaemon(this);
		this.workers = new HashMap<String, WorkerHandler>();
	}

	/**
	 * Add a WorkerHanlder into the its collection
	 * @param worker
	 */
	public void AddWorker(WorkerHandler worker)
	{
		this.workers.put(worker.getId(), worker);
	}

	/**
	 * Starts the daemon. It actually forwards all available 
	 * WorkFragmentRequests (actually {@link WorkRequest WorkRequests})
	 * to the available hanlders.
	 */
	@Override
	public void run()
	{
		while (true)
		{
			for (WorkerHandler wh : this.workers.values())
			{
				if (wh.isAvailable())
				{
					if (!this.server.getWorkFragmentRequests().isEmpty())
					{
						wh.getWork(this.server.getWorkFragmentRequests().poll());
						this.server.log("Info", "Just sent a work handler request to the worker " + wh.getId());
					}
				}
			}

			try
			{
				Thread.sleep(GridServerConfig.CHECKINTERVAL);
			}
			catch (InterruptedException e)
			{
			}
		}
	}

	/**
	 * Closes the daemon after sending close signal to all the hanlders it manages.
	 */
	@Override
	public void close()
	{
		synchronized (this.workers)
		{
			for (WorkerHandler wkh : this.workers.values())
			{
				wkh.close();
			}
			this.workers.clear();
		}
	}

	/**
	 * Deleted a WorkerHandler from its collection using its id
	 * @param id
	 */
	public void deleteWorkerHandler(String id)
	{
		this.workers.remove(id);
	}

	/**
	 * Returns the number of connected workers to the grid
	 * @return
	 */
	public int getNumberOfWorkers()
	{
		return this.workers.size();
	}

	/**
	 * Returns an available id to give to the {@link GridClient GridWorker} and the {@link WorkerHandler WorkerHanlder}
	 * @return
	 * 		a string with an available id
	 */
	public String getAvailableId()
	{
		String output = this.generateKey();
		while (this.workers.containsKey(output))
		{
			output = this.generateKey();
		}
		return output;
	}

	private String generateKey()
	{
		String output = "";
		String chars = "abcdefghijklmonpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		Random r = new Random();
		for (int i = 0; i < 32; i++)
		{
			output += chars.charAt(r.nextInt(chars.length()));
		}
		return "W" + output;
	}
}
